﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Serialization.Formatters.Binary;
using System.Diagnostics;

namespace SimpleText
{
    public partial class MainForm : Form
    {
        public StatisticsClass cipherSts = new StatisticsClass();
        public StatisticsClass plainSts = new StatisticsClass();
        public StatisticsClass attachedSts = new StatisticsClass();

        public MainForm()
        {
            InitializeComponent();
        }

        private void openToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (this.openFileDialog.ShowDialog() == DialogResult.OK)
            {
                this.symbolArrayControl1.Clear();
                cipherSts.Clear();
                plainSts.Clear();

                List<String> data = this.symbolArrayControl1.Data;
                Dictionary<String, String> dic = this.symbolArrayControl1.Dic;
                using (StreamReader sr = new StreamReader(this.openFileDialog.FileName))
                {
                    while (sr.Peek() >= 0)
                    {
                        data.Add(new String((Char)sr.Read(), 1));
                    }
                    sr.Close();
                }

                foreach (String cipher in data)
                {
                    dic[cipher] = "?";
                    cipherSts.Add(cipher, 1);
                }

                foreach (KeyValuePair<String, String> kvp in dic)
                    plainSts.Add(kvp.Value, cipherSts.At(kvp.Key));

                this.symbolArrayControl1.Apply();

                this.listView1.Items.Clear();

                Int64 cipherTotal = cipherSts.Total;
                Int64 plainTotal = plainSts.Total;
                Int64 attachedTotal = attachedSts.Total;
                foreach (KeyValuePair<String, String> kvp in dic)
                {
                    ListViewItem lvi = new ListViewItem(kvp.Key);
                    lvi.Name = kvp.Key;
                    lvi.SubItems.Add(kvp.Value);
                    lvi.SubItems.Add(String.Format("{0}/{1}", cipherSts.At(kvp.Key), cipherTotal));
                    lvi.SubItems.Add(String.Format("{0}/{1}", plainSts.At(kvp.Value), plainTotal));
                    lvi.SubItems.Add(String.Format("{0}/{1}", attachedSts.At(kvp.Value), attachedTotal));
                    this.listView1.Items.Add(lvi);
                }
            }
        }


        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void loadKeyToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (this.openKeyDialog.ShowDialog() == DialogResult.OK)
            {
                IFormatter formatter = new BinaryFormatter();
                Stream stream = new FileStream(this.openKeyDialog.FileName, FileMode.Open, FileAccess.Read, FileShare.Read);
                KeyClass key = (KeyClass)formatter.Deserialize(stream);
                stream.Close();

                Dictionary<String, String> dic = this.symbolArrayControl1.Dic;

                foreach (KeyClass.Replacement cp in key.Data)
                    dic[cp.cipher] = cp.plain;

                plainSts.Clear();
                foreach (KeyValuePair<String, String> kvp in dic)
                    plainSts.Add(kvp.Value, cipherSts.At(kvp.Key));

                this.symbolArrayControl1.Apply();

                Int64 cipherTotal = cipherSts.Total;
                Int64 plainTotal = plainSts.Total;
                Int64 attachedTotal = attachedSts.Total;

                Dictionary<String, ListViewItem> lvis = new Dictionary<String, ListViewItem>();
                foreach (ListViewItem lvi in this.listView1.Items)
                    lvis[lvi.Text] = lvi;

                foreach (KeyValuePair<String, String> kvp in dic)
                {
                    ListViewItem lvi = lvis[kvp.Key];
                    lvi.SubItems[1].Text = kvp.Value;
                    lvi.SubItems[2].Text = String.Format("{0}/{1}", cipherSts.At(kvp.Key), cipherTotal);
                    lvi.SubItems[3].Text = String.Format("{0}/{1}", plainSts.At(kvp.Value), plainTotal);
                    lvi.SubItems[4].Text = String.Format("{0}/{1}", attachedSts.At(kvp.Value), attachedTotal);
                }
            }
        }

        public void Apply()
        {
            this.symbolArrayControl1.Apply();

            Dictionary<String, String> dic = this.symbolArrayControl1.Dic;

            plainSts.Clear();
            foreach (KeyValuePair<String, String> kvp in dic)
                plainSts.Add(kvp.Value, cipherSts.At(kvp.Key));

            Int64 cipherTotal = cipherSts.Total;
            Int64 plainTotal = plainSts.Total;
            Int64 attachedTotal = attachedSts.Total;

            Dictionary<String, ListViewItem> lvis = new Dictionary<String, ListViewItem>();
            foreach (ListViewItem lvi in this.listView1.Items)
                lvis[lvi.Text] = lvi;

            foreach (KeyValuePair<String, String> kvp in dic)
            {
                ListViewItem lvi = lvis[kvp.Key];
                lvi.SubItems[1].Text = kvp.Value;
                lvi.SubItems[2].Text = String.Format("{0}/{1}", cipherSts.At(kvp.Key), cipherTotal);
                lvi.SubItems[3].Text = String.Format("{0}/{1}", plainSts.At(kvp.Value), plainTotal);
                lvi.SubItems[4].Text = String.Format("{0}/{1}", attachedSts.At(kvp.Value), attachedTotal);
            }
        }

        private void saveKeyToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (this.saveKeyDialog.ShowDialog() == DialogResult.OK)
            {
                Dictionary<String, String> dic = this.symbolArrayControl1.Dic;
                KeyClass key = new KeyClass();

                foreach (KeyValuePair<String, String> kvp in dic)
                    key.Add(kvp.Value, kvp.Key);

                IFormatter formatter = new BinaryFormatter();
                Stream stream = new FileStream(this.saveKeyDialog.FileName, FileMode.Create, FileAccess.Write, FileShare.None);
                formatter.Serialize(stream, key);
                stream.Close();
            }
        }

        private void buildToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (this.openFileDialog.ShowDialog() == DialogResult.OK)
            {
                StatisticsClass sts = new StatisticsClass();
                using (StreamReader sr = new StreamReader(this.openFileDialog.FileName))
                {
                    while (sr.Peek() >= 0)
                        sts.Add(new String((Char)sr.Read(), 1), 1);

                    sr.Close();
                }
                if (this.saveStatisticsDialog.ShowDialog() == DialogResult.OK)
                {
                    IFormatter formatter = new BinaryFormatter();
                    Stream stream = new FileStream(this.saveStatisticsDialog.FileName, FileMode.Create, FileAccess.Write, FileShare.None);
                    formatter.Serialize(stream, sts);
                    stream.Close();
                }
            }
        }

        private void saveAsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (this.saveFileDialog.ShowDialog() == DialogResult.OK)
            {
                List<String> data = this.symbolArrayControl1.Data;
                Dictionary<String, String> dic = this.symbolArrayControl1.Dic;
                using (StreamWriter sw = new StreamWriter(this.saveFileDialog.FileName))
                {
                    foreach (String cipher in data)
                        sw.Write(dic[cipher]);

                    sw.Close();
                }
            }
        }

        private void symbolArrayControl1_Changed(object sender, EventArgs e)
        {
            Dictionary<String, String> dic = this.symbolArrayControl1.Dic;
            plainSts.Clear();
            foreach (KeyValuePair<String, String> kvp in dic)
                plainSts.Add(kvp.Value, cipherSts.At(kvp.Key));

            Int64 cipherTotal = cipherSts.Total;
            Int64 plainTotal = plainSts.Total;
            Int64 attachedTotal = attachedSts.Total;

            Dictionary<String, ListViewItem> lvis = new Dictionary<String, ListViewItem>();
            foreach (ListViewItem lvi in this.listView1.Items)
                lvis[lvi.Text] = lvi;

            foreach (KeyValuePair<String, String> kvp in dic)
            {
                ListViewItem lvi = lvis[kvp.Key];
                lvi.SubItems[1].Text = kvp.Value;
                lvi.SubItems[2].Text = String.Format("{0}/{1}", cipherSts.At(kvp.Key), cipherTotal);
                lvi.SubItems[3].Text = String.Format("{0}/{1}", plainSts.At(kvp.Value), plainTotal);
                lvi.SubItems[4].Text = String.Format("{0}/{1}", attachedSts.At(kvp.Value), attachedTotal);
            }
        }

        private void listView1_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (this.listView1.SelectedItems.Count > 0)
            {
                ListViewItem lvi = this.listView1.SelectedItems[0];
                Dictionary<String, String> dic = this.symbolArrayControl1.Dic;

                dic[lvi.Text] = new String((Char)e.KeyChar, 1);

                plainSts.Clear();
                foreach (KeyValuePair<String, String> kvp in dic)
                    plainSts.Add(kvp.Value, cipherSts.At(kvp.Key));

                Int64 cipherTotal = cipherSts.Total;
                Int64 plainTotal = plainSts.Total;
                Int64 attachedTotal = attachedSts.Total;

                lvi.SubItems[1].Text = dic[lvi.Text];
                lvi.SubItems[2].Text = String.Format("{0}/{1}", cipherSts.At(lvi.Text), cipherTotal);
                lvi.SubItems[3].Text = String.Format("{0}/{1}", plainSts.At(dic[lvi.Text]), plainTotal);
                lvi.SubItems[4].Text = String.Format("{0}/{1}", attachedSts.At(dic[lvi.Text]), attachedTotal);

                this.symbolArrayControl1.Apply();
                e.Handled = true;
            }
        }

        private void attachToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (this.openStatisticsDialog.ShowDialog() == DialogResult.OK)
            {
                IFormatter formatter = new BinaryFormatter();
                Stream stream = new FileStream(this.openStatisticsDialog.FileName, FileMode.Open, FileAccess.Read, FileShare.Read);
                attachedSts = (StatisticsClass)formatter.Deserialize(stream);
                stream.Close();

                Dictionary<String, ListViewItem> lvis = new Dictionary<String, ListViewItem>();
                foreach (ListViewItem lvi in this.listView1.Items)
                    lvis[lvi.Text] = lvi;

                Dictionary<String, String> dic = this.symbolArrayControl1.Dic;
                Int64 attachedTotal = attachedSts.Total;

                foreach (KeyValuePair<String, String> kvp in dic)
                {
                    ListViewItem lvi = lvis[kvp.Key];
                    lvi.SubItems[4].Text = String.Format("{0}/{1}", attachedSts.At(kvp.Value), attachedTotal);
                }
            }
        }

        private static int SortDesc(KeyValuePair<String, Int64> x, KeyValuePair<String, Int64> y)
        {
            Int64 value = (y.Value - x.Value);
            if (value < 0) return -1;
            if (value > 0) return 1;
            return 0;
        }

        private void orderByStatisticsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (this.attachedSts.Total == 0)
            {
                attachToolStripMenuItem_Click(sender, e);
            }

            if (this.attachedSts.Total != 0)
            {
                List<KeyValuePair<String, Int64>> cipherList = new List<KeyValuePair<String, Int64>>();
                List<KeyValuePair<String, Int64>> plainList = new List<KeyValuePair<String, Int64>>();
                Dictionary<String, String> dic = this.symbolArrayControl1.Dic;

                foreach (KeyValuePair<String, Int64> kvp in cipherSts.Data)
                    cipherList.Add(kvp);

                foreach (KeyValuePair<String, Int64> kvp in attachedSts.Data)
                    plainList.Add(kvp);

                cipherList.Sort(SortDesc);
                plainList.Sort(SortDesc);

                for (int i = 0; i < cipherList.Count && i < plainList.Count; i++)
                    dic[cipherList[i].Key] = plainList[i].Key;

                Apply();
            }
        }

        private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            AboutBox about = new AboutBox();
            about.ShowDialog();
        }

        private void encryptToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (this.openFileDialog.ShowDialog() == DialogResult.OK)
            {
                Dictionary<String, String> dic = new Dictionary<String, String>();
                using (StreamReader sr = new StreamReader(this.openFileDialog.FileName))
                {
                    while (sr.Peek() >= 0)
                    {
                        String ch = new String((Char)sr.Read(), 1);
                        if (!dic.ContainsKey(ch)) dic.Add(ch, ch);
                    }
                    sr.Close();
                }

                alglib.hqrndstate state;
                alglib.hqrndrandomize(out state);


                for (int i = dic.Count; i-- > 0; )
                {
                    int a = alglib.hqrnduniformi(state, dic.Count);
                    int b = alglib.hqrnduniformi(state, dic.Count);
                    KeyValuePair<String, String> kvp1 = dic.ElementAt(a);
                    KeyValuePair<String, String> kvp2 = dic.ElementAt(b);
                    dic[kvp1.Key] = kvp2.Value;
                    dic[kvp2.Key] = kvp1.Value;
                }

                if (this.saveFileDialog.ShowDialog() == DialogResult.OK)
                {
                    StreamReader sr = new StreamReader(this.openFileDialog.FileName);
                    StreamWriter sw = new StreamWriter(this.saveFileDialog.FileName);
                    while (sr.Peek() >= 0)
                    {
                        sw.Write(dic[new String((Char)sr.Read(), 1)]);
                    }
                    sw.Close();
                    sr.Close();
                }
            }
        }

        private static int SortAsc0(ListViewItem x, ListViewItem y)
        {
            return x.Text.CompareTo(y.Text);
        }

        private static int SortAsc1(ListViewItem x, ListViewItem y)
        {
            return x.SubItems[1].Text.CompareTo(y.SubItems[1].Text);
        }

        private int SortDesc2(ListViewItem x, ListViewItem y)
        {
            if (cipherSts.At(x.Text) < cipherSts.At(y.Text)) return 1;
            if (cipherSts.At(x.Text) > cipherSts.At(y.Text)) return -1;
            return 0;
        }

        private int SortDesc3(ListViewItem x, ListViewItem y)
        {
            if (plainSts.At(x.SubItems[1].Text) < plainSts.At(y.SubItems[1].Text)) return 1;
            if (plainSts.At(x.SubItems[1].Text) > plainSts.At(y.SubItems[1].Text)) return -1;
            return 0;
        }

        private int SortDesc4(ListViewItem x, ListViewItem y)
        {
            if (attachedSts.At(x.SubItems[1].Text) < attachedSts.At(y.SubItems[1].Text)) return 1;
            if (attachedSts.At(x.SubItems[1].Text) > attachedSts.At(y.SubItems[1].Text)) return -1;
            return 0;
        }

        private void listView1_ColumnClick(object sender, ColumnClickEventArgs e)
        {
            List<ListViewItem> lvis = new List<ListViewItem>();
            foreach (ListViewItem lvi in this.listView1.Items)
                lvis.Add(lvi);
            Comparison<ListViewItem>[] comparison = 
            { 
                (Comparison<ListViewItem>)SortAsc0, 
                (Comparison<ListViewItem>)SortAsc1, 
                (Comparison<ListViewItem>)SortDesc2, 
                (Comparison<ListViewItem>)SortDesc3, 
                (Comparison<ListViewItem>)SortDesc4 
            };
            lvis.Sort(comparison[e.Column]);
            this.listView1.Items.Clear();
            this.listView1.Items.AddRange(lvis.ToArray());
        }

        private String hash(String s)
        {
            String h = "";
            List<Char> list = new List<Char>();
            foreach (Char ch in s.ToCharArray())
            {
                if (!list.Contains(ch))
                {
                    list.Add(ch);
                }
                h += ':' + list.IndexOf(ch).ToString() + ':';
            }
            return h;
        }

        private void guessWordsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (this.openFileDialog.ShowDialog() == DialogResult.OK)
            {
                Dictionary<String, List<String>> plains = new Dictionary<String, List<String>>();
                List<Int64> lengths = new List<Int64>();
                using (StreamReader sr = new StreamReader(this.openFileDialog.FileName))
                {
                    while (sr.Peek() >= 0)
                    {
                        String word = sr.ReadLine();
                        if (!lengths.Contains(word.Length))
                        {
                            lengths.Add(word.Length);
                        }
                        if (!plains.Keys.Contains(hash(word)))
                        {
                            plains.Add(hash(word), new List<String>());
                        }
                        if (!plains[hash(word)].Contains(word))
                        {
                            plains[hash(word)].Add(word);
                        }
                    }
                    sr.Close();
                }
                Int64 maxlength = 0;
                foreach (Int64 length in lengths)
                {
                    maxlength = Math.Max(maxlength, length);
                }
                Dictionary<String, String> hashes = new Dictionary<String, String>();
                List<String> data = this.symbolArrayControl1.Data;
                for (Int64 i = 0; i < data.Count; i++)
                {
                    String word = "";
                    for (Int64 j = 0; j < maxlength && i + j < data.Count; j++)
                    {
                        word += data[(int)(i + j)];
                        if (lengths.Contains(word.Length) && !hashes.Keys.Contains(word))
                        {
                            hashes.Add(word, hash(word));
                        }
                    }
                }

                Dictionary<String, List<String>> ciphers = new Dictionary<String, List<String>>();
                foreach (KeyValuePair<String, String> kvp in hashes)
                {
                    if (!ciphers.Keys.Contains(kvp.Value))
                    {
                        ciphers.Add(kvp.Value, new List<String>());
                    }
                    if (!ciphers[kvp.Value].Contains(kvp.Key))
                    {
                        ciphers[kvp.Value].Add(kvp.Key);
                    }
                }
                GuessWordsForm form = new GuessWordsForm();
                foreach (KeyValuePair<String, List<String>> kvp in plains)
                {
                    if (ciphers.Keys.Contains(kvp.Key))
                    {
                        List<String> plainlist = kvp.Value;
                        List<String> cipherlist = ciphers[kvp.Key];
                        foreach (String plain in plainlist)
                        {
                            foreach (String cipher in cipherlist)
                            {
                                ListViewItem lvi = new ListViewItem(plain);
                                lvi.SubItems.Add(cipher);
                                form.listView1.Items.Add(lvi);
                            }
                        }
                    }
                }
                if (form.ShowDialog() == DialogResult.OK)
                {
                    Dictionary<String, String> dic = this.symbolArrayControl1.Dic;
                    foreach (ListViewItem lvi in form.listView1.SelectedItems)
                    {
                        String plain = lvi.Text;
                        String cipher = lvi.SubItems[1].Text;
                        for (int i = 0; i < plain.Length; i++)
                        {
                            dic[cipher.Substring(i, 1)] = plain.Substring(i, 1);
                        }
                    }
                    Apply();
                }
            }
        }

        private void substitutionCipherToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Process.Start("http://en.wikipedia.org/wiki/Substitution_cipher");
        }
    }
}
